package com.dbflow5.config;

import ohos.app.Context;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;

/**
 * Description: The main configuration instance for DBFlow. This
 */
public class FlowConfig {

    Context context;
    Set<Class<DatabaseHolder>> databaseHolders;
    Map<Class<?>, DatabaseConfig> databaseConfigMap;
    boolean openDatabasesOnInit;

    public FlowConfig(Context context, Set<Class<DatabaseHolder>> databaseHolders, Map<Class<?>, DatabaseConfig> databaseConfigMap, boolean openDatabasesOnInit) {
        this.context = context;
        this.databaseHolders = databaseHolders;
        this.databaseConfigMap = databaseConfigMap;
        this.openDatabasesOnInit = openDatabasesOnInit;
    }

    public FlowConfig(Builder builder) {
        this.context = builder.context;
        this.databaseHolders = builder.databaseHolders;
        this.databaseConfigMap = builder.databaseConfigMap;
        this.openDatabasesOnInit = builder.openDatabasesOnInit;
    }

    public DatabaseConfig getConfigForDatabase(Class<?> databaseClass) {
        return databaseConfigMap.get(databaseClass);
    }

    /**
     * Merges two [FlowConfig] together by combining an existing config. Any new specified [DatabaseConfig]
     * will override existing ones.
     *
     * @param flowConfig flowConfig
     * @return FlowConfig
     */
    public FlowConfig merge(FlowConfig flowConfig) {
        databaseConfigMap.forEach((aClass, databaseConfig) -> {
            if(flowConfig.databaseConfigMap.get(aClass) != null) {
                databaseConfigMap.put(aClass, flowConfig.databaseConfigMap.get(aClass));
            }else {
                databaseConfigMap.put(aClass, databaseConfig);
            }
        });
        databaseHolders.addAll(flowConfig.databaseHolders);
        return new FlowConfig(flowConfig.context, databaseHolders, databaseConfigMap, flowConfig.openDatabasesOnInit);
    }

    public static class Builder {
        public Builder(Context context) {
            this.context = context.getApplicationContext();
        }

        private final Context context;
        private final Set<Class<DatabaseHolder>> databaseHolders = new HashSet<>();
        private final Map<Class<?>, DatabaseConfig> databaseConfigMap = new HashMap<>();
        private boolean openDatabasesOnInit = false;

        public Builder addDatabaseHolder(Class<DatabaseHolder> databaseHolderClass) {
            databaseHolders.add(databaseHolderClass);
            return this;
        }

        public Builder databaseHolder(Class<DatabaseHolder> clazz) {
            return addDatabaseHolder(clazz);
        }

        public Builder database(DatabaseConfig databaseConfig) {
            databaseConfigMap.put(databaseConfig.databaseClass, databaseConfig);
            return this;
        }

        //fn: DatabaseConfig.Builder.() -> Unit = {}
        public <T> Builder database(Class<T> clazz, Function<DatabaseConfig.Builder, Void> fn, DatabaseConfig.OpenHelperCreator openHelperCreator) {
            DatabaseConfig.Builder builder = DatabaseConfig.builder(clazz, openHelperCreator);
            fn.apply(builder);
            return database(builder.build());
        }

        public <T> Builder inMemoryDatabase(Class<T> clazz, Function<DatabaseConfig.Builder, Void> fn, DatabaseConfig.OpenHelperCreator openHelperCreator) {
            return database(clazz, builder -> {
                builder.inMemory();
                return null;
            }, openHelperCreator);
        }

        /**
         * openDatabasesOnInit
         *
         * @param openDatabasesOnInit true if we want all databases open.
         * @return True to open all associated databases in DBFlow on calling of [FlowManager.init]
         */
        public Builder openDatabasesOnInit(boolean openDatabasesOnInit) {
            this.openDatabasesOnInit = openDatabasesOnInit;
            return this;
        }

        public FlowConfig build() {
            return new FlowConfig(this);
        }
    }

    public static Builder builder(Context context) {
        return new Builder(context);
    }

    public static FlowConfig flowConfig(Context context, Function<FlowConfig.Builder, Void> fn) {
        FlowConfig.Builder builder = builder(context);
        fn.apply(builder);
        return builder.build();
    }
}
